package checked

import (
	"sort"
	"reflect"
	"kumachan/interpreter/lang/common/name"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/lang/common/limits"
	"kumachan/interpreter/compiler/checker/typsys"
)


type Expr struct {
	Type     typsys.Type  // change to CertainType when generics available
	Info     ExprInfo
	Content  ExprContent
}
type ExprInfo struct {
	Location  source.Location
}
func ExprInfoFrom(loc source.Location) ExprInfo {
	return ExprInfo { Location: loc }
}
type ExprContent interface { implExpr() }

func (UnitValue) implExpr() {}
type UnitValue struct {}

func (Argument) implExpr() {}
type Argument struct {}

func (Wrapper) implExpr() {}
type Wrapper struct {
	Inner  *Expr
}

func (FuncRef) implExpr() {}
type FuncRef struct {
	Name      name.FunctionName
	Implicit  [] *Expr
}

func (LocalRef) implExpr() {}
type LocalRef struct {
	Binding  *LocalBinding
}

func (Call) implExpr() {}
type Call struct {
	Callee    *Expr
	Argument  *Expr
}

func (Interface) implExpr() {}
type Interface struct {
	ConcreteValue  *Expr
	DispatchTable  *DispatchTable
}

func (InterfaceTransform) implExpr() {}
type InterfaceTransform struct {
	Argument  *Expr
	Path      InterfaceTransformPath
}
type InterfaceTransformPath ([] uint)

func (Enum) implExpr() {}
type Enum struct {
	Index  uint
	Value  *Expr
}

func (Tuple) implExpr() {}
type Tuple struct {
	Parallel  bool
	Elements  [] *Expr
}

func (TupleUpdate) implExpr() {}
type TupleUpdate struct {
	Tuple     *Expr
	Replaced  [] TupleUpdateElement
}
type TupleUpdateElement struct {
	Index  uint
	Value  *Expr
}

func (TupleGet) implExpr() {}
type TupleGet struct {
	Tuple  *Expr
	Index  uint
}

func (InterfaceMethod) implExpr() {}
type InterfaceMethod struct {
	Interface  *Expr
	Index      uint
}

func (List) implExpr() {}
type List struct {
	Items  [] *Expr
	Info   ListInfo
}
type ListInfo struct {
	Compact   bool
	ItemType  reflect.Type
}

func (InteriorRef) implExpr() {}
type InteriorRef struct {
	Base     *Expr
	Index    uint
	Table    *DispatchTable  // optional, used in dynamic cast
	Kind     InteriorRefKind
	Operand  InteriorRefOperand
}
type InteriorRefKind int
const (
	RK_Proj InteriorRefKind = iota
	RK_Case
	RK_InterfaceCase
)
type InteriorRefOperand int
const (
	RO_Record InteriorRefOperand = iota
	RO_EnumOrInterface
	RO_ProjRef
	RO_CaseRef
)

func (NumericLiteral) implExpr() {}
type NumericLiteral struct {
	Value  interface {}
}

func (StringLiteral) implExpr() {}
type StringLiteral struct {
	Value  string
}

func (StringFormatter) implExpr() {}
type StringFormatter struct {
	Template    string
	Parameters  [] uint
}

func (Lambda) implExpr() {}
type Lambda struct {
	In    ProductPatternInfo
	Out   *Expr
	Self  *LocalBinding
}

func (Block) implExpr() {}
type Block struct {
	LetList  [] Let
	Return   *Expr
}
type Let struct {
	Pattern ProductPatternInfo
	Input   *Expr
}
func NestedLambdaCalls(block Block, info ExprInfo) *Expr {
	var L = len(block.LetList)
	if L == 0 { return block.Return }
	var expr = block.Return
	for i := range block.LetList {
		var r = ((L - 1) - i)
		var let = block.LetList[r]
		var lambda = &Expr {
			Type: &typsys.NestedType { Content:
				typsys.Lambda {
					Input:  let.Input.Type,
					Output: expr.Type,
				} },
			Info: expr.Info,
			Content: Lambda {
				In:  let.Pattern,
				Out: expr,
			},
		}
		var call = &Expr {
			Type:    expr.Type,
			Info:    let.Input.Info,
			Content: Call {
				Callee:   lambda,
				Argument: let.Input,
			},
		}
		expr = call
	}
	return &Expr {
		Type:    expr.Type,
		Info:    info,
		Content: expr.Content,
	}
}

func (Switch) implExpr() {}
type Switch struct {
	Argument    *Expr
	HasDefault  bool
	Default     Branch
	Branches    map[uint] Branch
}
func (s *Switch) SortedBranchKeys() ([] uint) {
	var int_keys = make([] int, 0)
	for key := range s.Branches {
		int_keys = append(int_keys, int(key))
	}
	sort.Ints(int_keys)
	var uint_keys = make([] uint, len(int_keys))
	for i, k := range int_keys {
		uint_keys[i] = uint(k)
	}
	return uint_keys
}

func (Select) implExpr() {}
type Select struct {
	Arguments   [] *Expr
	HasDefault  bool
	Default     Branch
	Branches    map[SelectBranchKey] Branch
}
func (s *Select) SortedBranchKeys() ([] SelectBranchKey) {
	var keys = make(SelectBranchKeys, 0)
	for key := range s.Branches {
		keys = append(keys, key)
	}
	sort.Sort(keys)
	return keys
}

type SelectBranchKey ([limits.MaxSelectArity] uint)

type SelectBranchKeys ([] SelectBranchKey)
func (keys SelectBranchKeys) Len() int {
	return len(keys)
}
func (keys SelectBranchKeys) Less(u, v int) bool {
	for i := 0; i < limits.MaxSelectArity; i += 1 {
		var x = keys[u][i]
		var y = keys[v][i]
		if x < y {
			return true
		} else if x == y {
			continue
		} else {
			return false
		}
	}
	return false
}
func (keys SelectBranchKeys) Swap(u, v int) {
	var t = keys[u]
	keys[u] = keys[v]
	keys[v] = t
}

type Branch struct {
	Lambda
}
func (b *Branch) ToExpr() *Expr {
	return &Expr {
		Type:    b.Out.Type,
		Info:    b.Out.Info,
		Content: *b, // note: *Branch will also implement ExprContent
	}
}


